        SUBT    > Sources.OSFind - Open and Close

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; +                                                                           +
; +                             O P E N    S W I                              +
; +                                                                           +
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; FindEntry. Vectored SWI level entry
; =========

; Corresponds to BBC OSFIND with path + termination extensions

FindEntry ROUT

        TST     r0, #&FF                ; r0b is mode for Open/Close
        BNE     TopOpenFile             ; CLOSE#r1 ?

; .............................................................................
;
; TopCloseFile. SWI level entry
; ============
;
; Close one file or all files open on any Filing System

; In    r1b = file handle to close

; Out   VC: file closed, r0 = 0
;       VS: fail

TopCloseFile NewSwiEntry "r0-r1"

 [ debugosfindentry
 DREG r1,"OSClose: handle "
 DREG sp, "Entry sp = "
 ]
        AND     r1, r1, #&FF            ; r1b is file handle
        BL      CloseFile

      [ Version >= 160 :LAND: Version < 162
        BL      checkallclosed          ; issue fsfunc_LastFileClosed if so
      ]

 [ debugosfind
 BVC %FT90
 DREG sp, "OSClose exit sp = "
 LDR r14, globalerror
 DREG r14, "globalerror "
90
 ]
        SwiExit

; .............................................................................
;
; checkallclosed
; ==============
;
; Check whether there are any open files on the temp Filing System

; In    [tempfs] = fscb^ of temporary filing system
;       globalerror != 0 => error pending

; Out   fsfunc_LastFileClosed issued if there are no open files on that FS
;       VS if globalerror set up (previous error maintained if present)

        [ Version >= 160 :LAND: Version < 162

checkallclosed  ENTRY  "r1, r2, scb, fscb"

        LDR     fscb, tempfs
        TEQ     fscb, #0
        EXIT    EQ                      ; no filing system

        MOV     r1, #MaxHandle          ; Loop over handles in stream table
        MOV     r2, #1                  ; no matches found yet

10      BL      FindStream              ; Get scb^ for this handle, VClear
        BEQ     %FT60

        LDR     r14, scb_fscb           ; Is handle on this Filing System ?
        TEQ     r14, fscb
        MOVEQ   r2, #0                  ; yes!

; Ignore any errors that this may give; we catch them on exit

60      SUBS    r1, r1, #1              ; Last valid handle = 1
        BNE     %BT10                   ; VClear from SUBS

        TEQ     r2, #0
        BEQ     %FT70

        Push    "r0-r6"
        MOV     r0, #fsfunc_LastFileClosed
        MOV     r6, #0                  ; no special field
        BL      CallFSFunc_Given        ; use fscb
        STRVS   r0, [sp]
        Pull    "r0-r6"

70      TEQ     fp, #0                  ; Can do bugger all if called in a
        EXIT    EQ                      ; brain damaged state

        LDR     r14, globalerror        ; Need to check this now
        CMP     r14, #0                 ; VClear
        SETV    NE                      ; VSet if there's an error lurking
        EXIT
      ]

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; CloseFile
; =========
;
; Close one file or all files open on temp Filing System

; In    r1 =           0 -> close all files on the temporary Filing System
;           1..MaxHandle -> file handle to close

; Out   VC: file closed
;       VS: fail

CloseFile

 [ debugosfind
 DREG r1,"CloseFile: handle "
 ]
        CMP     r1, #0
        BEQ     CloseAllFiles

; .............................................................................
;
; CloseThisFile
; =============
;
; Close file of a given handle. File may be on any Filing System

; In    r1[1..MaxHandle] is FileSwitch handle

; Out   VC: file closed
;       VS: failed to close, or no file on this channel

CloseThisFile ENTRY "scb"

        BL      FindValidStream         ; Returns scb^ if VClear
        BLVC    FlushAndCloseStream     ; Gets streaminfo itself
 [ appendhandle
        BLVS    AppendHandleToError
 ]
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; CloseAllFiles
; =============
;
; Flush and close all files on the temporary Filing System (*CLOSE low-level)

; In    no parms

; Out   VC: all files closed
;       VS: fail, no particular handle faulted

CloseAllFiles ENTRY "fscb"

        BL      ReadTempFS
        EXIT    VS
        MOV     fscb, r0
        TEQ     fscb, #Nowt
        BLNE    CloseAllFilesOnThisFS
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; CloseAllFilesEverywhere
; =======================
;
; Flush and close all files on all Filing Systems (*SHUT equivalent)

; In    no parms, fp not necessarily valid (called on death)

; Out   VC: all files closed
;       VS: failed to close one or more files

CloseAllFilesEverywhere ENTRY "r1, scb, fscb"

        MOV     fscb, #0

05      MOV     r1, #MaxHandle          ; Loop over handles in stream table

10      BL      FindStream              ; Get scb^ for this handle, VClear
        BEQ     %FT60

        TEQ     fscb, #0                ; fscb = 0 -> match all Filing Systems
        LDRNE   r14, scb_fscb           ; Is handle on this Filing System ?
        TEQNE   r14, fscb
        LDRNE   r14, scb_fscbForContents; Is handle used inside this filing system ?
        TEQNE   r14, fscb
        BLEQ    FlushAndCloseStream     ; Gets stream info itself
 [ appendhandle
        BLVS    AppendHandleToError
 ]

; Ignore any errors that this may give; we catch them on exit

60      SUBS    r1, r1, #1              ; Last valid handle = 1
        BNE     %BT10                   ; VClear from SUBS

        TEQ     fp, #0                  ; Can do bugger all if called in a
        EXIT    EQ                      ; brain damaged state

        LDR     r14, globalerror        ; Need to check this now
        CMP     r14, #0                 ; VClear
        SETV    NE                      ; VSet if there's an error lurking
        EXIT

; .............................................................................
; In    fscb = that to close and flush all files on

CloseAllFilesOnThisFS ALTENTRY ; Does need to preserve registers

 [ debugosfind
 DREG fscb,"Closing all files on fscb ",cc
 Push r1
 ADD r1, fscb, #fscb_name
 DSTRING r1, ", Filing System "
 Pull r1
 ]
        B       %BT05

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; TryGetFileClosed
;
; In
;   r1 = path tail
;   r6 = scb/special
;   fscb
;
; Out
;   error possible
;
TryGetFileClosed ENTRY "r1-r3"
        MOV     r3, sp
        BL      int_ConstructFullPathOnStack
        MOV     r2, sp
        Push    "r3"
        MOV     r3, #0
        MOV     r1, #Service_CloseFile
        SWI     XOS_ServiceCall
        LDR     sp, [sp]
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; TopOpenFile. SWI level entry
; ===========
;
; Corresponds to current BBC OSFIND ~0 with path + termination extensions

; Open on temporary Filing System (generally current)

; In    r0 =  OSFIND type. Must now have read and/or write request
;       r1 -> space,CR,LF or 0 terminated filename (will be translated)
;       r2 -> optional path variable name or string to use (depending on r0)

; Out   VC: r0 is file handle, 0 if file not opened
;       VS: fail

TopOpenFile NewSwiEntry "r1-r10" ; was r1,scb

 [ debugosfindentry
 DREG r0,"OSFind: mode ",cc
 DSTRING r1,", filename "
 ]
        BL      OpenFile                ; Resultis r0
 [ debugosfindentry
 DREG r0, "OSFind return: r0="
 ]

80      SwiExit

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; OpenFile
; ========
;
; Attempt to open file using temporary Filing System

; In    r0 = open mode
;       r1 -> filename (raw)
;       r2 -> optional path variable or string (depending on r0)

; Out   VC: r0 = FileSwitch handle, 0 if not found
;       VS: naff

OpenFile ENTRY "r0-r7, scb" ; We poke the result into the stack

 [ debugosfind
 Push r1
 TST r0, #open_write
 MOVNE r1, #"W"
 MOVEQ r1, #"w"
 BREG  r1,"OpenFile: mode ",cc
 TST r0, #open_read
 MOVNE r1,#"R"
 MOVEQ r1,#"r"
 BREG r1,,cc
 TST r0, #open_nodir
 BEQ %FT01
 MOV r1, #"F"
 BREG r1,,cc
01
 TST r0, #open_mustopen
 BEQ %FT01
 MOV r1,#"M"
 BREG r1,,cc
01
 AND r1, r0, #open_pathbits
 TEQ r1, #open_pathvar
 BEQ %FT01
 MOV r1,#"V"
 BREG r1,,cc
01
 AND r1, r0, #open_pathbits
 TEQ r1, #open_pathstring
 BEQ %FT01
 MOV r1,#"S"
 BREG r1,,cc
01
 AND r1, r0, #open_pathbits
 TEQ r1, #open_nopath
 BEQ %FT01
 MOV r1,#"N"
 BREG r1,,cc
01
 Pull r1
 DSTRING r1,", filename "
 ]
        TST     r0, #open_unused        ; Any duff bits set ?
        BNE     %FA80

        MOV     r7, r0                               ; Save Open mode bits

; Decide where to try and open the file (OpenIn/OpenUp)

        TST     r7, #open_read          ; Can't look on any path if we don't
        ANDNE   r14, r7, #open_pathbits ; have a read component
        ADDNE   pc, pc, r14, LSL #2

        B       OpenUsingNoPath         ; Also serves to get addressing right

 assert open_pathbits = 2_11 ; For jump table

        B       OpenUsingDefaultPath
        B       OpenUsingPathString
        B       OpenUsingPathVariable
        B       OpenUsingNoPath


80      addr    r0, ErrorBlock_BadModeForOSFind
        BL      CopyError
        EXIT

; .............................................................................

OpenUsingNoPath ; Try to open this file

        addr    r2, anull               ; Null path string

OpenUsingPathString ; Try to open using given path string

        MOV     r3, r2
        MOV     r2, #NULL
        B       DoTheOpen

OpenUsingPathVariable ; Try to open using given path variable

        addr    r3, anull
        B       DoTheOpen

OpenUsingDefaultPath ; Try to open using File$Path

        addr    r2, FilePathVariableNameCR
        ADD     r3, r0, #(FilePathVariableDefault-FilePathVariableNameCR)

; .............................................................................
; In    r1 -> user's filename
;       r2 -> path varname
;       r3 -> default path
;       r7 = open bits

DoTheOpen

        ADR     r0, PassedFilename
        ADR     r4, FullFilename
        MOV     r5, #0
        AND     r14, r7, #open_write :OR: open_read
        TEQ     r14, #open_write
        ORREQ   r5, r5, #TopPath_NoMultiParts
        ADR     r6, SpecialField
        BL      TopPath_DoBusinessToPath
        EXIT    VS
 [ debugosfind
        DSTRING r1,"File tail found:"
 ]

        LDR     r2, PassedFilename      ; Filename to use on error in open
        AND     r5, r7, #open_read :OR: open_write
        BL      DoTheOpen_Common
        BVS     %FT90

        BL      JunkFileStrings

        STRVC   r0, [sp]                ; Return handle to stacked r0
        EXIT    VC

        BL      DeallocateStream        ; Only needs scb^ valid
        EXIT

90
        BL      JunkFileStrings
        EXIT


; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;       r0 = objecttype of file
;       r1 = filename tail to use
;       r2 = filename to use if error
;       r5 = open mode (&40, &80, &C0)
;       r6 = special field/scb^ for file
;       r7 = open mode as supplied by the user
;       fscb^ set up for file

; Out   r0 = new FileSwitch handle

; Requested op  NoFile  IsDir   LWR lWR LwR lwR LWr lWr Xwr
; ============  ======  =====   === === === === === === ===

; OpenIn        han 0   han x   wR  wR  wR  wR  err err err

; OpenUp        han 0   error   WR  WR  wR  wR  err Wr  err

; OpenOut       han x   error   err WR  err wR  err Wr  err
;              created

; Notes:
;       OpenIn fails if no read permission
;       OpenOut fails if file is locked (silly to set 0 extent; equiv. delete)
;       OpenOut preserves file load/exec/attr. Always sets scb_modified
;       All fail if no read or write permission

DoTheOpen_Common ENTRY "r0-r5" ; Result poked into stacked r0

 [ debugosfind
 DSTRING r1, "DoTheOpen_Common - tail:",cc
 DSTRING r2, " error name:",cc
 DREG r5, " exact mode:",cc
 DREG r7, " with bits:",cc
 DREG fscb," fscb=",cc
 DREG r0, " objecttype="
 ]

        BL      TryGetFileClosed
        EXIT    VS

        BL      AllocateStream          ; Allocate an external handle to r3
        EXIT    VS

        STR     r3, [sp]                ; Result in stacked r0
        BL      TryToOpenFile           ; (FS need this passed in r3 on Open)
        BVS     %FT75                   ; Deallocate stream if failed
        STREQ   r1, [sp]                ; If not found. NB. This is FS handle 0
        BEQ     %FT75                   ; Deallocate stream if not opened

; Succesfully opened the file !

        LDR     r14, [sp, #5*4]
        TEQ     r14, #open_read         ; If opening for reading, don't permit
        BICEQ   r0, r0, #fsopen_WritePermission ; writing to file

        BL      StuffTheStreamInfo      ; Wobble the bits into place, deallocs
        EXIT    VS                      ; if error

        LDR     r14, [sp, #5*4]
        TEQ     r14, #open_write        ; If OpenOut, set scb_modified
        LDREQ   r14, scb_status         ; so we will stamp on closing
        ORREQ   r14, r14, #scb_modified
        STREQ   r14, scb_status
        EXIT


75      MOV     r0, psr                 ; Deallocate exit
        BL      DeallocateStream
        TEQVCP  r0, #0
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In    r1, r6, fscb set
;       r0 = exact OSFind op

; Out   r0 = new FileSwitch handle

OpenFileForCopy ENTRY "r1-r7" ; Untrusting of what goes in in the depths ...

        ; Generate all but the error name for the open
        MOV     r5, r0
        MOV     r0, #object_file        ; (honest, guv!)
        ORR     r7, r5, #open_mustopen  ; Mustn't fail now !

        ; Generate the error name on the stack
        MOV     r4, sp
        BL      int_ConstructFullPathOnStack
        MOV     r2, sp

        ; Open it
        BL      DoTheOpen_Common

        ; Drop the error name
        MOV     sp, r4
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; TryToOpenFile
; =============
;
; In    r0  = file type from OSFile 5 committed in ExPath routine
;       r1 -> filename to use for open
;       r2 -> filename to use if dir error
;       r3  = external handle
;       r5  = low level mode for open. Either &40, &80 or &C0
;       r6  = special field/scb^ to use
;       r7  = full bits for open
;       fscb^ set up (not nowt)

; Out   VS: failed
;       VC: r1 = Filing System handle (NE), or 0 (EQ - not found)

; If opened:
; r0 is the attributes + device/system identifier for this file - normally zero
; r1 is a valid Filing System handle to stuff in our scb
; r2 is the buffer size to use with this file
; r3 is the size of the file
; r4 is the space allocated to the file
; r5 is the path tail used to open
; fscb is the fscb used to open

TryToOpenFile ENTRY "r2,r5,r6,r7,r8" ; r2 needs to be kept around

        ; Check there IS a filing system to be based upon
        TEQ     fscb, #Nowt
        BLEQ    SetErrorNoSelectedFilingSystem
        EXIT    VS

        ; If the base filing system says always open it, do so
        LDR     r14, [fscb, #fscb_info]
        TST     r14, #fsinfo_alwaysopen
        BNE     %FT40

        ; If open out
        TEQ     r5, #open_write
        BNE     %FT10

        ; open out MUST open the file
        ORR     r7, r7, #open_mustopen

        ; If it doesn't already exist
        TEQ     r0, #object_nothing
        BNE     %FT20

        ; Create the object
        Push    "r3"
        MOV     r0, #fsfile_Create
        LDR     r2, =FileType_Data
        BL      SReadTime
        MOV     r4, #0
        MOV     r5, #0
        BL      CallFSFile_Given
        Pull    "r3"

        ; Create OK, skip other checks on file and go into the open sequence
        MOVVC   r5, #open_write
        BVC     %FT40

        ; FS barfed on the create (eg *create NFS#Printer: doesn't work too well), so
        ; try to open with open_write instead.
        MOV     r5, #open_write
        MOV     r0, #0
        STR     r0, globalerror
        CLRV
        MOV     r0, #open_write
        B       %FT50

10
        ; If object doesn't exist now, then its either an error or return handle 0
        TEQ     r0, #object_nothing
        BNE     %FT20
        TST     r7, #open_mustopen
        MOVEQ   r1, #0
        LDRNE   r1, [sp]
        BLNE    SetMagicFileNotFound
        EXIT

20
        ; The object exists, check for it being a pure directory in the wrong circumstances
        TEQ     r0, #object_directory
        BNE     %FT40
        TEQ     r5, #open_read
        TSTEQ   r7, #open_nodir
        BNE     %FT30
        LDR     r14, [fscb, #fscb_info]
        TST     r14, #&ff
        BNE     %FT40                   ; is dir, but opening read without winge on non-MultiFS
30
        ; is dir, but wasn't opening read without winge on non-MultiFS
        LDR     r1, [sp]
        BL      SetMagicIsADirectory
        EXIT

40
        ; object exists and its variety (file/dir) is OK.
        ; Copy the open mode and translate open_write to open_update in this situation
        MOV     r0, r5
        TEQ     r0, #open_write
        MOVEQ   r0, #open_update

50
        ; It's time to get the filing system to open the object.
        ; Store the actual path tail used for returning to the caller.
        STR     r1, [sp, #1*4]
        BL      CallFSOpen
        EXIT    VS

        ; Force extent to 0 when doing an open_write really
        TEQ     r5, #open_write
        MOVEQ   r3, #0

        ; Check for non-zero handle and return if is non-zero
        TEQ     r1, #0
        STRNE   r2, [sp]
        EXIT    NE

        ; File open returned a 0 handle - check for the validity of this
        TST     r7, #open_mustopen
        EXIT    EQ

        ; Open failed to open the file and this isn't allowed!
        addr    r0, MagicErrorBlock_CantOpenFile
        LDR     r1, [sp]
        BL      MagicCopyError
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; StuffTheStreamInfo
; ==================

; In    scb^ valid and:

; r0 is the attributes + device/system identifier for this file - normally zero
; r1 is a valid Filing System handle to stuff in our scb
; r2 is the buffer size to use with this file
; r3 is the size of the file
; r4 is the space allocated to the file
; r5 is the path tail used in the open
; r6 is the scb^/special field used in the open
; fscb is the fscb used for the open

; Out   stream deallocated if error

StuffTheStreamInfo ENTRY "r1"

        STR     r1, scb_fshandle
        STR     r3, scb_extent
        STR     r4, scb_allocsize

        SUB     r14, r2, #1             ; Store the mask we will be using
        STR     r14, scb_bufmask

        MOV     r1, #0                  ; Calculate log2(buffersize)
10      MOVS    r14, r14, LSR #1        ; Can't be 0
        ADD     r1, r1, #1
        BNE     %BT10
        STR     r1, scb_shift

        MOV     r14, #0                 ; Initial PTR of file is 0
        STR     r14, scb_fileptr

        STR     fscb, scb_fscb          ; Associate with the fs used to open it

        AND     r14, r0, #fsopen_DeviceIdentity
        STR     r14, scb_devid

        MOV     r14, #0                 ; Set funky bits, not all in order
        TST     r0, #fsopen_WritePermission
        ORRNE   r14, r14, #scb_write
        TST     r0, #fsopen_ReadPermission
        ORRNE   r14, r14, #scb_read
        TST     r0, #fsopen_IsDirectory
        ORRNE   r14, r14, #scb_directory
        TST     r0, #fsopen_UnbufferedGBPB
        ORRNE   r14, r14, #scb_unbuffgbpb
        TST     r0, #fsopen_Interactive
        ORRNE   r14, r14, #scb_interactive

; Now have converted returned access + status into scb status

        TEQ     r2, #0          ; Keep buffered/unbuffered state too
        ORREQ   r14, r14, #scb_unbuffered
        STR     r14, scb_status ; Store access mode, buffer state, EOF clear

 [ Max_BuffSize >= 2048
        TEQNE   r2, #2048       ; vital for efficient CDFS access
 ]
        TEQNE   r2, #1024       ; Limited range of sensible buffer sizes
        TEQNE   r2, #512
        TEQNE   r2, #256
        TEQNE   r2, #128
        TEQNE   r2, #64         ; Below this seems senseless !
        BNE     %FA85           ; Deallocate stream if failed

; Store the path strings for this object
        LDRB    r14, [fscb, #fscb_info]
        TEQ     r14, #0
        STREQ   r6, scb_special
        ADRNE   r0, scb_special
        MOVNE   r1, r6
        BLNE    SNewString
        ADRVC   r0, scb_path
        MOVVC   r1, r5
        BLVC    SNewString
        BVS     %FA90

; Clear any contents out - this isn't a MultiFS file yet
        MOV     r14, #Nowt
        STR     r14, scb_fscbForContents
        STR     r14, scb_ImageList

; Lastly, put the scb into the stream table now that it's kosher

        ADR     r14, streamtable
        LDR     r1, scb_exthandle
        STR     scb, [r14, r1, LSL #2] ; Kapow !
 [ debugosfind
 DREG r1,"Opened file; exthandle "
 ]
        EXIT


85      addr    r0, ErrorBlock_BadBufferSizeForStream
        BL      CopyError

90      BL      DeallocateStream        ; Only needs scb^ valid to do this
        SETV
        EXIT

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        LTORG

        END
